﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;

using SharedGenomics.Domain;
using SharedGenomics.Core;
using SharedGenomics.Workbench.Core;
using SharedGenomics.Workbench.Views;
using SharedGenomics.Workbench.Models;
using SharedGenomics.Workbench.PresentationModel;
using SharedGenomics.DataTransfer;

namespace SharedGenomics.Workbench.Presenters
{
    public class AnalysisResultsFilterPresenter : Presenter
    {
        private JobType _anyJobType = (JobType)(-1);
        private JobStatus _anyJobStatus = (JobStatus)(-1);
        private DateTime _anySubmissionTime = DateTime.MinValue;

        public AnalysisResultsFilterPresenter(IAnalysisResultsFilterView view, IJobDetailsRepository jobRepository, IAvailableJobsModel model)
            : base(view)
        {
            this.FilterView = view;
            this.JobRepository = jobRepository;
            this.Model = model;

            view.Model = model;
            view.ViewLoaded += this.View_ViewLoaded;
        }

        private void View_ViewLoaded(object sender, EventArgs e)
        {
            this.FilterView.ToggleVisibleClicked += (o, ea) => { this.CollapsedToggleRequested.Raise(this); };
            this.FilterView.FindJobsClicked += View_FindJobsClicked;
            this.FilterView.LoadJobClicked += View_LoadJobClicked;
            this.FilterView.SelectedJobChanged += (o, ea) => { this.FilterView.IsLoadJobEnabled = this.FilterView.SelectedJob != null; };

            //set combobox items on view
            this.SetViewSubmissionTimes();
            this.SetViewJobStatuses();
            this.SetViewJobTypes();

            this.FilterView.IsLoadJobEnabled = false;
        }

        private void View_FindJobsClicked(object sender, EventArgs e)
        {
            //create criteria
            JobSpecification spec = this.GetJobSpecification();

            this.FindJobsInProgress(true);

            //get matching jobs from repository and set on model
            this.BackgroundExecutor.Execute(() =>
            {
                return this.JobRepository.GetUserJobs(spec);
            },
            or =>
            {
                //re-enable load button and hide progress bar
                this.FindJobsInProgress(false);

                if (or.Error == null)
                {
                    or.Result.BindAction(userJobs =>
                    {
                        this.Model.Clear();
                        foreach (var job in userJobs)
                            this.Model.Add(job);
                    });
                }
                else
                {
                    this.FilterView.DisplayError(String.Format("Unable to find jobs: {0}", or.Error.Message));
                }
            });
        }

        private void FindJobsInProgress(bool inProgress)
        {
            //disable load button and show progress bar
            this.FilterView.IsFindJobsEnabled = !inProgress;
            this.FilterView.IsFindJobsProgressBarVisible = inProgress;
        }

        private void View_LoadJobClicked(object sender, EventArgs e)
        {
            JobDetails selectedJob = this.FilterView.SelectedJob;
            Debug.Assert(selectedJob != null);

            this.JobResultsLoadRequested.Raise(this, new JobResultsLoadEventArgs(selectedJob.Id));
        }

        private JobSpecification GetJobSpecification()
        {
            var selectedTime = this.FilterView.SelectedSubmissionTime;
            JobType selectedJobType = this.FilterView.SelectedJobType;
            JobStatus selectedJobStatus = this.FilterView.SelectedJobStatus;

            return new JobSpecification()
            {
                SubmittedAfter = selectedTime == this._anySubmissionTime ? (DateTime?)null : selectedTime,
                JobType = selectedJobType == this._anyJobType ? (JobType?)null : selectedJobType,
                Status = selectedJobStatus == this._anyJobStatus ? (JobStatus?)null : selectedJobStatus
            };
        }

        public void ToggleCollapsed()
        {
            this.FilterView.ToggleVisible();
        }

        public event EventHandler CollapsedToggleRequested;
        public event EventHandler<JobResultsLoadEventArgs> JobResultsLoadRequested;

        private void SetViewSubmissionTimes()
        {
            DateTime currentTime = TimeService.Now();
            List<ValueDisplayItem<DateTime>> jobSubmitTimes = new List<ValueDisplayItem<DateTime>>
            {
                new ValueDisplayItem<DateTime>(this._anySubmissionTime, "Any"),
                new ValueDisplayItem<DateTime>(currentTime.AddDays(-1), "1 day ago"),
                new ValueDisplayItem<DateTime>(currentTime.AddDays(-2), "2 days ago"),
                new ValueDisplayItem<DateTime>(currentTime.AddDays(-7), "1 week ago"),
                new ValueDisplayItem<DateTime>(currentTime.AddMonths(-1), "1 month ago"),
            };

            this.FilterView.JobSubmissionTimes = jobSubmitTimes;
            this.FilterView.SelectedSubmissionTime = currentTime.AddDays(-2);
        }

        private void SetViewJobTypes()
        {
            var typeDisplayItems = this.GetEnumValues<JobType>().Select(jt => new ValueDisplayItem<JobType>(jt, jt.GetDisplayString()));
            this.FilterView.JobTypes = new[] { new ValueDisplayItem<JobType>(this._anyJobType, "Any") }.Concat(typeDisplayItems).ToArray();
            this.FilterView.SelectedJobType = this._anyJobType;
        }

        private void SetViewJobStatuses()
        {
            var statusDisplayItems = this.GetEnumValues<JobStatus>().Select(js => new ValueDisplayItem<JobStatus>(js, js.ToString()));
            this.FilterView.JobStatuses = new[] { new ValueDisplayItem<JobStatus>(this._anyJobStatus, "Any") }.Concat(statusDisplayItems).ToArray();
            this.FilterView.SelectedJobStatus = JobStatus.Finished;
        }

        private IEnumerable<T> GetEnumValues<T>()
        {
            return Enum.GetValues(typeof(T)).Cast<T>();
        }

        public int MinimumViewHeight
        {
            get { return this.FilterView.MinimumHeight; }
        }
        private IAnalysisResultsFilterView FilterView { get; set; }
        private IJobDetailsRepository JobRepository { get; set; }
        private IAvailableJobsModel Model { get; set; }
    }
}
